<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>创建对象</title>
</head>

<body>
  <script>
    //创建对象
    //1.工厂模式
    function createPerson(name, age, job) {
      let o = new Object();
      o.name = name;
      o.age = age;
      o.job = job;
      o.sayName = function () {
        console.log(this.name);
      }
      return o;
    }
    let p = createPerson("hhh", 22, "engineer");
    console.log(p);
    //2.构造函数模式
    function Person(name, age, job) {
      this.name = name;
      this.age = age;
      this.job = job;
      this.sayName = sayName;
    }

    function sayName() {
      console.log(this.name);
    }
    let p1 = new Person("sloan", 22, "Doctor");
    p1.sayName();//sloan

    let o = new Object();
    //这里的调用将对象o指定为Person()内部的this值,因此代码执行完毕后,所有属性和sayName()方法都会添加到对象o上.
    Person.apply(o, ["xxx", 22, "teacher"]);
    o.sayName(); //xxx
    //3.原型模式
    function Student(name, age, score) {
      this.name = name;
      this.age = age;
      this.score = score;
    }
    //给prototype的原型上添加方法.
    Student.prototype.sayName = function () {
      console.log(this.name);
    }
    let s1 = new Student("lisi", 22, 99);
    console.log(s1.sayName());//lisi
    console.log(Student.prototype.isPrototypeOf(s1));//true
    console.log(Object.getPrototypeOf(s1) === Student.prototype);//true
    //Object.create()创建新对象
    let biped = {
      numLegs: 2
    }
    let person = Object.create(biped);//为biped提供原型
    console.log(Object.getPrototypeOf(person) === biped);
    //原型层级
    function Dog() {
      Dog.prototype.name = "wen";
      Dog.prototype.age = 3;
      Dog.prototype.sex = "female";
      Dog.prototype.bark = function () {
        console.log("wangwang~")
      }
    }
    let d1 = new Dog();
    let d2 = new Dog();
    d1.name = "mei";
    console.log(d1.name, d2.name); //mei wen
    delete d1.name;
    console.log(d1.name); //wen 删除实例上的name属性,就会继续搜索至原型对象.
    //hasOwnProperty()方法,检测实例上是否有指定的属性.
    let d3 = new Dog();
    d3.name = "ddd";
    console.log(d1.hasOwnProperty("name"), d3.hasOwnProperty("name"));//false true
    //in操作符
    console.log("name" in d1, "name" in d2, "name" in d3); //都是true
    //判断是否是原型对象.只要in操作符返回true且hasOwnProperty()返回false,就说明是一个原型对象
    function hasPrototypeProperty(obj, name) {
      return !obj.hasOwnProperty(name) && (name in obj);
    }
    console.log(hasPrototypeProperty(d1, "name"), hasPrototypeProperty(d3, "name"));//true false
    //Object.keys()
    let keys = Object.keys(Dog.prototype);
    console.log(keys);//['name', 'age', 'sex', 'bark']
    let d4 = new Dog();
    d4.name = "dd"; d4.age = 22; let d4keys = Object.keys(d4);
    console.log(d4keys);//['name', 'age']
    //Object.getOwnPropertyNames(obj):会得到实例的所有属性
    let allKeys = Object.getOwnPropertyNames(Dog.prototype);
    console.log(allKeys);//['constructor', 'name', 'age', 'sex', 'bark']
    //针对ES6的Symbol类型
    let k1 = Symbol("k1"), k2 = Symbol("k2");
    let o1 = {
      [k1]: "k1",
      [k2]: "k2"
    }
    console.log(Object.getOwnPropertySymbols(o1));//[Symbol(k1), Symbol(k2)]
    //对象迭代
    let o2 = {
      name: "sss",
      age: 22,
      sex: "male"
    }
    console.log(Object.values(o2), Object.entries(o2));//返回值数组和键值对数组
    console.log(Object.values(o2)[0] === o2.name, Object.entries(o2)[0][1] === o2.name);//true true
    //其他原型语法
    function Cat() { }
    Cat.prototype = {
      name: "Tom",
      age: 22,
      job: "catch mouse",
      bark() {
        console.log("miaomiaomiao~~~");
      }
    };
    //使用Object.defineProperty来恢复constructor.使其不可枚举,并且能指向Cat构造函数.
    Object.defineProperty(Cat.prototype, "constructor", {
      enumerable: false,
      value: Cat
    });

    let c1 = new Cat();
    Cat.prototype.eat = function () {
      console.log("eat fish");
    }
    c1.eat();
    //给原型添加方法.
    String.prototype.startsWith = function (text) {
      return this.indexOf(text) === 0;
    }
    let msg = "Hello Sloan!";
    console.log(msg.startsWith("Hello")); //true
    //原型问题
    function Person1() { }
    Person1.prototype = {
      name: "Sloan",
      age: 22,
      job: "software Engineer",
      friends: ["sherry", "pubby"],
      sayName() {
        console.log(this.name);
      }
    }
    //重写原型上的构造器属性constructor.
    Object.defineProperty(Person1.prototype, "constructor", {
      enumerable: false,
      value: Person1
    })
    //Person1的实例
    let p2 = new Person1();
    let p3 = new Person1();
    p2.friends.push("Van");
    //p2和p3都有Van,实际开发中通常不单独使用原型模式. 
    console.log(p2.friends,p3.friends); //(3) ['sherry', 'pubby', 'Van'] (3) ['sherry', 'pubby', 'Van']
  </script>
</body>

</html>